/**
  ETFAna project, Anyang Normal University && IMP-CAS
  \class ETFDetector
  \brief a general detector class, which can hold a list of sub-detectors, like
  the sturcture of the TTask. Note that this is an abstract base class
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2021-10-27
  \date 2021-12-11 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                   </tr>
  <tr>  <td>2021-10-27   <td>Asia Sun    <td>file created                  </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <iostream>
#include <TGraph.h>
#include "ETFRun.h"
#include "ETFDetector.h"
#include "ETFHit.h"
#include "ETFMath.h"
#include "ETFDetectorManager.h"
#include "ETFParaManager.h"
#include "ETFYaml.h"
#include "ETFMsg.h"
#include "ETFHPTDCCh.h"

using std::cout;
using std::endl;

static const double DEGREE = ETFMath::DEGREE();
static const double HPI = ETFMath::Pi() / 2.;
ETFDetector::ETFDetector(const char *name) : fName(name), fDelay(0.), fPos{0},
fAng{0}, fTrigLB(-999.), fTrigHB(-999.), fIsFired(false), fGDumb(nullptr){
  fRun = ETFRun::Instance();
  fTree = fRun->GetTree();
  fpm = ETFParaManager::Instance();
  fdm = ETFDetectorManager::Instance();

  fChIdConfig = (*fpm->GetChIdPara())[name];
  const string qname = string(name) + 'Q'; // for Q (amplitude)
  if((*fpm->GetChIdPara())[qname])
    fQChIdConfig = (*fpm->GetChIdPara())[qname];
  fDetId = fdm->GetDetectorMap().size() + 1; // so that a valid uid will not be

  const ETFYaml &detPosConfig = (*fpm->GetDetPosPara())[name];
  for(int i = 0; i < 3; i++){
    if(detPosConfig["pos"]) fPos[i] = detPosConfig["pos"][i].as<double>();
    if(detPosConfig["ang"]) fAng[i] = detPosConfig["ang"][i].as<double>()*DEGREE;
  } // end for over i
  // pass user delay parameters //
  const ETFYaml &delayConfig = (*fpm->GetDelayPara())[name];
  if(delayConfig) fDelay = delayConfig.as<double>();
  // pass user trig bound parameters //
  const string trigb = string(name) + "TrigB";
  if((*fpm->GetMisPara())[trigb]){
    const ETFYaml &trigBConfig = (*fpm->GetMisPara())[trigb];
    fTrigLB = trigBConfig[0].as<double>();
    fTrigHB = trigBConfig[1].as<double>();
  } // end if
} // end ctor

ETFDetector::~ETFDetector(){
  if(fGDumb) delete fGDumb;
} // end dtor

void ETFDetector::Initialize(){
  fIsFired = false;
} // end member function Initialize

void ETFDetector::SetChMap(int chid, unsigned uid, bool overwrite){
  if(fdm->GetUID(chid) && !overwrite)
    ETFMsg::Error("ETFDetector", "SetChMap: chid %d has been assigned", chid);
  fdm->SetChMap(chid, uid);
} // end member function SetChMap

// to tell whether ch is within time window around the trig, to filter multihit
bool ETFDetector::TrigBoundCheck(ETFHPTDCCh *ch){
  if(-999. == fTrigLB || -999. == fTrigHB) return true; // not used
  return ETFMath::Within(ch->GetLeadingEdge(), fTrigLB, fTrigHB);
} // end member function TrigBoundCheck

void ETFDetector::TransformToLabRef(double *p) const{
  static double pr[3];
  ETFMath::rotate(pr, p, fAng);
  for(int i = 3; i--;) p[i] = pr[i] + fPos[i]; // then translate: a + l
} // end member function TransformToLabRef

void ETFDetector::GetPosition(double *p) const{
  memcpy(p, fPos, sizeof(fPos));
} // end member function GetPos

void ETFDetector::GetAng(double *a) const{
  memcpy(a, fAng, sizeof(fAng));
} // end member function GetAng

// p1[0-4]: {k, b, k2, b2}; p: the hit point
// p: cross point of trk3d with the detector's sensitive area
// ifY: whether to calculate Y coordinate
void ETFDetector::GetHitPosition(const double *p1, double *p, bool ifY) const{
  if(fabs(fAng[0]) < 5.e-3){ // the det is approximately in the xOy coordinate plane
    p[2] = fPos[2];
    p[0] = p1[0]*p[2]+p1[1];
  } // end if
  else{
    const double k = p1[0], b = p1[1];
    // l: x=ktz+bt: the function of TOFWall
    const double kt = tan(fAng[0]+HPI), bt = fPos[0] - kt*fPos[2]; // b = x-kz
    // calculate the cross point of l: x=kz+b and lt: x=ktz+bt
    const double de = (k-kt);
    if(fabs(de) < 1.e-20) p[0] = p[2] = 1.e20;
    else{
      p[2] = (bt-b)/de;
      p[0] = (bt*k-b*kt)/de;
    } // end else
  } // end else

  if(ifY) p[1] = p1[2]*p[2]+p1[3]; // y = k2z+b2
} // end member function GetHitPosition
// distance of the hit point to the center of the detector
// in zOx plane (lx) and zOy plane (ly)
void ETFDetector::GetHitPositionL(const double *p1, double &lx, double &ly) const{
  double p[3]{}; // coordinate of the hit
  GetHitPosition(p1, p, true);
  lx = sqrt(ETFMath::sqr(p[2]-fPos[2]) + ETFMath::sqr(p[0]-fPos[0]));
  if(p[0] < fPos[0]) lx *= -1.;
  ly = p[1] - fPos[1]; // y = k2z+b2; ly = y - yDet
} // end member function GetHitPositionL
// distance of the hit point to the center of the detector in zOx plane
void ETFDetector::GetHitPositionL(const double *p1, double &lx) const{
  double p[3]{}; // coordinate of the hit
  GetHitPosition(p1, p);
  lx = sqrt(ETFMath::sqr(p[2]-fPos[2]) + ETFMath::sqr(p[0]-fPos[0]));
  if(p[0] < fPos[0]) lx *= -1.;
} // end member function GetHitPositionL


void ETFDetector::TransformToLabRef(ETFHit *h) const{
  return TransformToLabRef(h->GetPosition());
} // end member function TransformToLabRef

void ETFDetector::Draw(){
  if(!fGDumb){
    fGDumb = new TGraph();
    fGDumb->SetName(GetName());
  } // end if
  ConstructGraph();
  if(fGDumb->GetN()) fGDumb->Draw("psame");

  DrawHits();
} // end member function Draw

void ETFDetector::Print(){
  cout << "\033[36;1m=========== DETECTOR ";
  cout << GetName() << " PRINT ============\033[0m" << endl;
  cout << "Pos(mm): (x, y, z): " << "(" << fPos[0] << ", " << fPos[1];
  cout << ", " << fPos[2] << ")" << endl;
  cout << "Ang(deg): (Phi, Theta, Psi): " << "(" << fAng[0]/DEGREE;
  cout << ", " << fAng[1]/DEGREE << ", " << fAng[2]/DEGREE << ")" << endl;
} // end member function Print
